namespace Pp.UI.Models
{
    using System;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Web.Mvc;

    public class ExpressionHelper
    {
        #region Property or Action to Name
        public static string GetPropertyName<T>(Expression<Func<T, object>> member) where T : class
        {
            var info = GetMemberInfo(member) as PropertyInfo;
            if (info == null)
                throw new ArgumentException("Member is not a property");

            return info.Name;
        }

        public static string GetMethodName<T>(Expression<Func<T, object>> action) where T : Controller
        {
            return GetActionInfo(action).ActionName;
        }

        public static string GetMethodName<T>(Expression<Action<T>> action) where T : Controller
        {
            return GetActionInfo(action).ActionName;
        }
        #endregion

        public static MemberInfo GetMemberInfo(Expression member)
        {
            if (member == null) throw new ArgumentNullException("member");

            var lambda = member as LambdaExpression;
            if (lambda == null) throw new ArgumentException("Not a lambda expression", "member");

            MemberExpression memberExpr = null;

            if (lambda.Body.NodeType == ExpressionType.Convert)
                memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
            else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
                memberExpr = lambda.Body as MemberExpression;

            if (memberExpr == null) throw new ArgumentException("Not a member access", "member");

            return memberExpr.Member;
        }

        public static ActionInfo GetActionInfo<TController>(Expression<Func<TController, object>> action) where TController : Controller
        {
            var methodCall = action.Body as MethodCallExpression;
            if (methodCall == null)
                throw new ArgumentException("Expression must be method call.");

            var controllerName = typeof(TController).Name;
            if (!controllerName.ToLowerInvariant().EndsWith("controller"))
                throw new ArgumentException("Controller name must be end with controller.");

            controllerName = controllerName.Substring(0, controllerName.Length - "controller".Length);
            if (controllerName.Length == 0)
                throw new ArgumentException("Cannot route to class named 'Controller'");

            return new ActionInfo { ControllerName = controllerName, ActionName = methodCall.Method.Name };
        }

        public static ActionInfo GetActionInfo<TController>(Expression<Action<TController>> action) where TController : Controller
        {
            var methodCall = action.Body as MethodCallExpression;
            if (methodCall == null)
                throw new ArgumentException("Expression must be method call.");

            var controllerName = typeof(TController).Name;
            if (!controllerName.ToLowerInvariant().EndsWith("controller"))
                throw new ArgumentException("Controller name must be end with controller.");

            controllerName = controllerName.Substring(0, controllerName.Length - "controller".Length);
            if (controllerName.Length == 0)
                throw new ArgumentException("Cannot route to class named 'Controller'");

            return new ActionInfo { ControllerName = controllerName, ActionName = methodCall.Method.Name };
        }
    }
}